package com.netty.esc.config.shiro.realm;

import com.alibaba.fastjson.JSONObject;
import com.netty.esc.common.global.UserCache;
import com.netty.esc.dao.mapper.UserMapper;
import com.netty.esc.entity.model.UserInfoDTO;
import com.netty.esc.entity.pojo.UserInfo;
import com.netty.esc.entity.pojo.UserRole;
import com.netty.esc.service.http.UserRoleService;
import com.netty.esc.utils.JwtToken;
import com.netty.esc.utils.TokenUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * Shiro从从Realm获取安全数据（如用户、角色、权限）
 * ，就是说SecurityManager要验证用户身份，那么它需要从Realm获取相应
 * 的用户进行比较以确定用户身份是否合法；也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作；
 * 可以把Realm看成DataSource，即安全数据源。Realm主要有两个方法：
 * doGetAuthorizationInfo(获取授权信息)
 * doGetAuthenticationInfo(获取身份验证相关信息)：
 * @Version 1.0
 **/
@Configuration
@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Resource
    UserRoleService userRoleService;

    @Resource
    TokenUtil tokenUtil;

    @Resource
    UserMapper userMapper;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 授权(验证权限时调用)
     * 这个方法就会从数据库中读取我们所需要的信息，最后封装成SimpleAuthorizationInfo返回去
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserInfo userInfo = UserCache.getCurrentUser();
        UserInfoDTO userInfoDTO = new UserInfoDTO();
        BeanUtils.copyProperties(userInfo,userInfoDTO);

        //添加角色及权限
        UserRole userRole = this.userRoleService.getUserRole(userInfoDTO);
        userRole.getRoles().forEach(authorizationInfo::addRole);
        userRole.getPermissions().forEach(authorizationInfo::addStringPermission);

        return authorizationInfo;
    }

    /**
     * 认证(登录时调用)
     */
    @SneakyThrows
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)  {
        final String token = (String) authenticationToken.getCredentials();

        log.info("正在对token进行登录验证...");
        JSONObject object = tokenUtil.tokenAnalysis(token);
        if (Objects.isNull(object)){
            log.error("token为{}的用户token错误",token);
            throw new Exception("token错误...");
        }
        String telephone = object.getString("telephone");
        UserInfo userInfo = this.userMapper.getUserInfoByTelephone(telephone);

        if (Objects.isNull(userInfo)){
            log.error("token为{}的用户信息不存在",token);
            throw new UnknownAccountException("用户信息不存在...");
        }

         //缓存添加当前用户信息
         UserCache.addCurrentUser(userInfo);

        return new SimpleAuthenticationInfo(token,token,"UserRealm");
    }
}
